import tkinter as tk
from tkinter import scrolledtext
import json
import random
import math

# 生成二维数据
def set_data():
    rows = 6
    cols = 36
    arr = []
    for i in range(rows):
        sub_arr = []
        for j in range(cols):
            rand = round(random.random() * 100)
            # 10%的概率可能产生异常值
            if random.random() > 0.9:
                rand = rand * 10
            sub_arr.append(rand)
        arr.append(sub_arr)
    # 逐行显示数组
    arr_text.delete('1.0', tk.END)
    arr_text.insert(tk.END, json.dumps(arr).replace('],', '],\n'))

# 抽样方法
def set(v):
    str_data = arr_text.get('1.0', tk.END).strip()
    if len(str_data) == 0:
        set_data()
        str_data = arr_text.get('1.0', tk.END).strip()
    data = json.loads(str_data)
    arr = []
    if v == "分层":  # 分层抽样
        for i in range(len(data)):
            layer_len = len(data[i])
            # 每层最多10个
            max_num = 10
            if max_num > layer_len:
                max_num = layer_len
            for j in range(max_num):
                rand_index = random.randint(0, layer_len - 1)
                arr.append(data[i][rand_index])  # 可以重复选中
    elif v == "系统":  # 系统抽样
        # 二维数组转成一维
        sort_arr = []
        for sub_arr in data:
            sort_arr.extend(sub_arr)
        # 对数组进行排序
        sort_arr.sort()
        step = 1  # 预计步长
        num = 37  # 样本数
        sort_len = len(sort_arr)
        if sort_len / num > 1:
            step = math.ceil(sort_len / num)
        # 从头开始指定间距挑选样本
        for j in range(0, len(sort_arr), step):
            arr.append(sort_arr[j])
    elif v == "整群":  # 整群抽样
        group_index = random.randint(0, len(data) - 1)
        arr.extend(data[group_index])
    else:  # 随机抽样
        one_arr = []
        for sub_arr in data:
            one_arr.extend(sub_arr)
        size = 37  # 至少37个样本
        one_len = len(one_arr)
        if size > one_len:
            size = one_len
        # 非重复抽取
        rands = []  # 抽取名单
        for j in range(size):
            rand_index = random.randint(0, one_len - 1)
            if rand_index in rands:
                # 如果重复抽中就增加一次抽取次数
                size += 1
                # 为了防止数据过少而陷入死循环，指定最大次数
                if size > 100:
                    break
            else:
                rands.append(rand_index)  # 添加样本目录
                arr.append(one_arr[rand_index])  # 添加样本数据
    # 输出样本数据
    show_label.config(text=str(arr))
    # 显示原数组与抽样后的数组
    print(v, data, arr)

# 剔除异常数据
def exc(data):
    # 平均数
    av = averaged(data)
    # 方差
    va = variance(data)
    # 标准差
    sd = int(math.sqrt(va))
    # 循环检测异常数据
    arr = []
    for i in range(len(data)):
        # 只保留当前值与平均数的差小于3倍的标准差的数据
        if abs(data[i] - av) < 3 * sd:
            arr.append(data[i])
    return arr

# 平均数
def averaged(a):
    # 先求和
    m = sum([float(x) for x in a])
    # 再求平均数
    av = m / len(a)
    return av

# 方差
def variance(a):
    # 先求和
    av = averaged(a)
    # 最后求方差
    n = 0
    for i in range(len(a)):
        e = float(a[i]) - av  # 与平均数的差
        n += e * e
    v = n / len(a)
    return v

# 创建主窗口
root = tk.Tk()
root.title("样本选择")

# 创建文本框用于显示数组数据
arr_text = scrolledtext.ScrolledText(root, width=40, height=10)
arr_text.pack()

# 创建标签用于显示抽样结果
show_label = tk.Label(root, text="")
show_label.pack()

# 创建按钮用于随机生成数据
generate_button = tk.Button(root, text="随机生成数据", command=set_data)
generate_button.pack()

# 创建下拉菜单用于选择抽样方法
options = ["随机", "分层", "系统", "整群"]
selected_option = tk.StringVar(root)
selected_option.set(options[0])
option_menu = tk.OptionMenu(root, selected_option, *options, command=set)
option_menu.pack()

# 运行主循环
root.mainloop()